package com.czy.java.thread;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author czy
 * @date 2022-2-22 17:43
 */
public class VolatileDemo2 {
    /**
     * volatile关键字
     * 1.内存可见性
     * 2.防止指令重排
     * 3.非原子性
     */

    public volatile int data = 0;

    private AtomicInteger data2 =new AtomicInteger(0);

    public void add(){
        data ++;
        data2.getAndIncrement();
    }

    /**
     * 1.实验一: 定义一个共享资源对象resources,其成员变量data值为0,创建20个线程,每个线程调用1000次add方法对data加1,
     * 正常来说最后data的值回事20000
     * 2.实验一结果: 每次输出结果都要小于20000
     * 3.原因分析: volatile并不能保证操作的原子性,i++虽然只有一行代码,但是 翻译成机器码执行后会分成
     * getfield(获取值) iadd(操作值) putfield(回写值) 3个操作,假如data等于0,线程1在执行到第二部操作值时,此时cpu的执行时间正好被线程2抢夺过去,
     * 线程2会将data的值修改为1,然后线程1在抢夺回cpu操作时间后会再次修改data的值为1,那么前面线程1执行的一次操作就无效了,导致最终输出结果会小于2000
     *
     * 4.解决方法:
     *  方法一: add方法用synchronized修饰   (重量级锁,浪费性能)
     *  方法二: data类型修改为Au
     *
     */
    public static void main(String[] args) {
        VolatileDemo2 resources = new VolatileDemo2();

        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int m = 0; m < 1000; m++) {
                    resources.add();
                }
            },"线程"+i).start();
        }

        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println("所有线程结束, 此时data变量的值为: "+ resources.data);
        System.out.println("所有线程结束, 此时data2变量的值为: "+ resources.data2);
    }

}
